package wordit.impl;

import java.util.ArrayList;
import java.util.List;

import wordit.ea.EvolutionaryAlgorithmListener;
import wordit.ea.Generation;
import wordit.ea.Genome;

/**
 * Collects max-fitness and avg-fitness per-generation per-trial.
 * 
 * When all trials are done, prints the average max-fitness and avg-fitness per
 * generation.
 * 
 * @author itamar
 * 
 */
public class DataListener implements EvolutionaryAlgorithmListener {

	private List<List<Double>> maxFitness = new ArrayList<List<Double>>();
	private List<List<Double>> avgFitness = new ArrayList<List<Double>>();
	private Genome bestGenome;

	@Override
	public void onStart() {
		maxFitness.add(new ArrayList<Double>());
		avgFitness.add(new ArrayList<Double>());
		System.err.print("Gen#: ");
	}

	@Override
	public void onNewGeneration(Generation newGeneration) {
		maxFitness.get(maxFitness.size() - 1).add(
				newGeneration.getBestGenome().getFitness());

		avgFitness.get(avgFitness.size() - 1).add(
				newGeneration.evalAverageFitness());

		if (bestGenome == null
				|| bestGenome.getFitness() < newGeneration.getBestGenome()
						.getFitness())
			bestGenome = newGeneration.getBestGenome();

		System.err.print(newGeneration.getCurrGenNumber() + " ");
	}

	@Override
	public void onStop() {
		System.err.printf("\nTrial %d done. Last genome: %f %f\n", maxFitness.size(),
				getLastForTrial(maxFitness.size() - 1, maxFitness),
				getLastForTrial(avgFitness.size() - 1, avgFitness));
	}

	private Double getLastForTrial(int trialIndex,
			List<List<Double>> dataColumns) {
		List<Double> trialResults = dataColumns.get(trialIndex);
		return trialResults.get(trialResults.size() - 1);
	}

	public void onAllTrialsDone() {
		final int generationsPerTrial = maxFitness.get(0).size();

		for (int genNumber = 0; genNumber < generationsPerTrial; ++genNumber)
			System.out.printf("%d\t%f\t%f\n", genNumber, getAverageData(
					maxFitness, genNumber), getAverageData(avgFitness,
					genNumber));

		System.err.printf("Best genome:\n%s\n", bestGenome);
	}

	private Double getAverageData(List<List<Double>> column,
			int generationNumber) {
		final int trialsDone = column.size();
		double avg = 0;

		for (int trial = 0; trial < trialsDone; ++trial)
			avg += column.get(trial).get(generationNumber);

		return avg / (double) trialsDone;
	}
}
